﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using ClientSide.Communication;
using System.Threading.Tasks;
using CustomProvider;
using CustomProvider.Event;
using System.Collections.ObjectModel;
using ClientSide.Model;

namespace ClientSide
{
    /// <summary>
    /// Interaction logic for ConfigurationView.xaml
    /// </summary>
    public partial class ConfigurationView : Window
    {
        #region Fields

        /// <summary>
        /// Player collection for binding to listview
        /// </summary>
        public ObservableCollection<Player> AlreadyPlayers { get; set; }

        /// <summary>
        /// The socket for communication with the Server
        /// </summary>
        private AsynSocket _asynSocket = new AsynSocket();

        /// <summary>
        /// The window for playing game
        /// </summary>
        private MainWindow _frmMainWindow = null;

        /// <summary>
        /// ID of player
        /// </summary>
        private string _idPlayer = string.Empty;

        /// <summary>
        /// Defines contanst for this class
        /// </summary>
        public static class Const
        {
            /// <summary>
            /// The message INVITETOPLAY which is sent to the server
            /// </summary>
            public const string INVITETOPLAY_MSG = "INVITETOPLAY";

            /// <summary>
            /// The message REPLY_INVITETOPLAY which is sent to the server
            /// </summary>
            public const string REPLY_INVITETOPLAY_MSG = "REPLY_INVITETOPLAY";
        }

        #endregion

        /// <summary>
        /// The default constructor
        /// </summary>
        public ConfigurationView()
        {
            InitializeComponent();
            
            this.SubscribeNotifyDisplayMessageOnGUIEvent();
            this.SubscribeNotifyConnectStatusEvent();
            this.SubscribeNotifyStartGameEvent();
            this.SubscribeNotifyTimeOutEvent();
            this.SubscribeNotifyUpdateAlreadyPlayerListEvent();
            this.SubscribeNotifyInviteToPlayEvent();
            this.SubscribeNotifyRejectInviteToPlayEvent();

            txtServerStatus.Text = DateTime.Now.ToString() + " Waiting for start..." + Environment.NewLine;
            this.AlreadyPlayers = new ObservableCollection<Player>();
            this.DataContext = this;
        }

        /// <summary>
        /// Notify the invitation to play has been rejected
        /// </summary>
        private void SubscribeNotifyRejectInviteToPlayEvent()
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyRejectInviteToPlayEvent>().Subscribe((EventData) =>
            {
                Action action = () =>
                {
                    MessageBox.Show("Your invitation has been rejected", "Information", MessageBoxButton.OK);
                    btnPlay.IsEnabled = true;
                };
                this.Dispatcher.Invoke(action);
            }, false);
        }

        /// <summary>
        /// Notify the the invitation to play
        /// </summary>
        private void SubscribeNotifyInviteToPlayEvent()
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyInviteToPlayEvent>().Subscribe((EventData) =>
            {
                Action action = () =>
                {
                    var compPlayers = EventData.Split(new char[] { '#' });
                    var idPlayerSend = compPlayers[0];
                    var idPlayerReceive = compPlayers[1];
                    var msg = string.Format("You has been received an invitation to play from the player {0} at the IPAddress {1}, do you want to play with this player?",
                        CommonMethodProvider.GetPlayerNameFromIDPlayer(idPlayerSend),
                        CommonMethodProvider.GetIPAddressFromIDPlayer(idPlayerSend));
                    var result = MessageBox.Show(msg, "Information", MessageBoxButton.YesNo) == MessageBoxResult.Yes ? true : false;
                    if (result == false)
                    {
                        txtServerStatus.AppendText(DateTime.Now.ToString() + " " + string.Format("The invitation to play from the player {0} at the IPAddress {1} has been rejected",
                        CommonMethodProvider.GetPlayerNameFromIDPlayer(idPlayerSend),
                        CommonMethodProvider.GetIPAddressFromIDPlayer(idPlayerSend)));
                    }
                    this._asynSocket.SendSync(string.Format("{0}:{1}#{2}#{3}", Const.REPLY_INVITETOPLAY_MSG, idPlayerSend, idPlayerReceive, result.ToString()));
                };
                this.Dispatcher.Invoke(action);
            }, false);
        }

        /// <summary>
        /// Notify the connection status
        /// </summary>
        private void SubscribeNotifyUpdateAlreadyPlayerListEvent()
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyUpdateAlreadyPlayerListEvent>().Subscribe((EventData) =>
            {
                Action action = () =>
                {
                    var sPlayer = EventData;
                    var compPlayers = sPlayer.Split(new char[] { ',' }).Where(n => n.NonEquals(string.Empty));
                    this.AlreadyPlayers.Clear();

                    foreach (string idPlayer in compPlayers)
                    {
                        if (idPlayer.Equals(this._idPlayer) == false)
                        {
                            var playerName = CommonMethodProvider.GetPlayerNameFromIDPlayer(idPlayer);
                            var playerIP = CommonMethodProvider.GetIPAddressFromIDPlayer(idPlayer);
                            this.AlreadyPlayers.Add(new Player(playerName, playerIP));
                        }
                    }     
                };
                this.Dispatcher.Invoke(action);
            }, false);
        }

        /// <summary>
        /// Display message on user interface
        /// </summary>
        private void SubscribeNotifyStartGameEvent()
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyStartGameEvent>().Subscribe((EventData) =>
            {
                Action action = () =>
                {
                    // Display message in GUI
                    var msg = DateTime.Now.ToString() + " Received question from the Server - Subject: " +
                        EventData.Suggestion + " Suggestion: " + EventData.Suggestion;
                    this.txtServerStatus.AppendText(msg + Environment.NewLine);

                    // Show Game window
                    if (_frmMainWindow == null)
                    {
                        this.Hide();
                        this._frmMainWindow = new MainWindow()
                        {
                            GameInfoParam = EventData,
                            IDPlayer = this._idPlayer,
                            AsynSocket = this._asynSocket,
                            ActionCloseForm = () =>
                            {
                                this.Show();
                                btnPlay.IsEnabled = true;
                                this.AlreadyPlayers.Clear();
                                this.txtServerStatus.AppendText(DateTime.Now.ToString() + " The Game has been closed" + Environment.NewLine);
                                this.btnStart.IsEnabled = true;
                                return;
                            }
                        };
                        this._frmMainWindow.Show();
                    }
                };
                this.Dispatcher.Invoke(action);
            }, false);
        }

        /// <summary>
        /// Raise timeout when estblishment communication
        /// </summary>
        private void SubscribeNotifyTimeOutEvent()
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyTimeOutEvent>().Subscribe((EventData) =>
            {
                Action action = () =>
                {
                    this.btnStart.IsEnabled = true;
                    this.txtServerStatus.AppendText(EventData + Environment.NewLine);
                };
                this.Dispatcher.Invoke(action);
            }, false);
        }

        /// <summary>
        /// Display message on user interface
        /// </summary>
        private void SubscribeNotifyDisplayMessageOnGUIEvent()
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyDisplayMessageOnGUIEvent>().Subscribe((EventData) =>
            {
                Action action = () =>
                {
                    this.txtServerStatus.AppendText(EventData + Environment.NewLine);
                };
                this.Dispatcher.Invoke(action);
            }, false);
        }

        /// <summary>
        /// Notify the connection status
        /// </summary>
        private void SubscribeNotifyConnectStatusEvent()
        {
            EventAggregatorFactory.Instance.GetEvent<NotifyConnectStatusEvent >().Subscribe((EventData) =>
            {
                Action action = () =>
                {
                    if (EventData.IsConnected)
                        this.btnStart.IsEnabled = false;
                    else
                        this.btnStart.IsEnabled = true;

                    this.txtServerStatus.AppendText(EventData.MsgTxt + Environment.NewLine);
                };
                this.Dispatcher.Invoke(action);
            }, false);
        }

        /// <summary>
        /// Validate input by user such as port and server address
        /// </summary>
        /// <returns>true: valid and false: invalid</returns>
        private bool ValidateInputs(ref string errTxt)
        {
            if (txtServerAddress.Text.Equals(string.Empty))
            {
                errTxt = "Server address is empty";
                return false;
            }

            if (txtServerPort.Text.Equals(string.Empty))
            {
                errTxt = "Server port is empty";
                return false;
            }
            
            if (txtServerPort.Text.IsNumeric() == false)
            {
                errTxt = "Server port must be numeric";
                return false;
            }

            if (txtPlayerName.Text.Equals(string.Empty))
            {
                errTxt = "The Player Name is empty";
                return false;
            }
            return true;
        }

        private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            var errTxt = string.Empty;
            if (!ValidateInputs(ref errTxt))
            {
                MessageBox.Show(errTxt, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            // Get server information
            var serverAddress = txtServerAddress.Text.Trim();
            var serverPort = int.Parse(txtServerPort.Text.Trim());
            var ownerIP = CommonMethodProvider.RetrieveIPAddress();
            var playerName = txtPlayerName.Text.Trim();
            this._idPlayer = playerName + "@" + ownerIP;

            Action action = () =>
            {
                this._asynSocket.StartConnecting(serverAddress, serverPort, playerName, ownerIP);
            };
            Task task = new Task(action);
            task.Start();
        }

        private void btnExit_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            var playerIPAddress = CommonMethodProvider.RetrieveIPAddress();
            var playerName = txtPlayerName.Text.Trim();
            this._asynSocket.Disconnect(playerName, playerIPAddress);
        }

        private void btnClear_Click(object sender, RoutedEventArgs e)
        {
            txtServerStatus.Clear();
        }

        private void btnPlay_Click(object sender, RoutedEventArgs e)
        {
            if (gridAlreadyPlayer.SelectedIndex == -1)
                return;
            btnPlay.IsEnabled = false;
            var player = gridAlreadyPlayer.SelectedItem as Player;
            this._asynSocket.SendSync(string.Format("{0}:{1}#{2}", Const.INVITETOPLAY_MSG, this._idPlayer, player.IDPlayer));
        }
    }
}
